module share.utility;

import std.string, std.conv, std.stream, std.regex, std.datetime, std.range;
import std.ascii: toUpper;
import std.c.windows.windows; //win32.windows;


/*void CenterDialog(HWND hParentWnd, HWND hWnd)
{
    RECT rcMainWnd, rcDlg;
    
    GetWindowRect(hParentWnd, &rcMainWnd);
    GetWindowRect(hWnd, &rcDlg);
    
    MoveWindow(hWnd, rcMainWnd.left + (((rcMainWnd.right - rcMainWnd.left) - (rcDlg.right - rcDlg.left)) / 2), 
                rcMainWnd.top + (((rcMainWnd.bottom - rcMainWnd.top) - (rcDlg.bottom - rcDlg.top)) / 2), 
                (rcDlg.right - rcDlg.left), (rcDlg.bottom - rcDlg.top), FALSE);
}*/

long FileTimeToQuadWord(PFILETIME pFileTime)
{  
    long qw;
    
    qw = pFileTime.dwHighDateTime;
    qw <<= 32;
    qw |= pFileTime.dwLowDateTime;
    
    return qw;
}

void QuadTimeToFileTime(long qw, PFILETIME pFileTime)
{
    pFileTime.dwHighDateTime = cast(uint)(qw >> 32);
    pFileTime.dwLowDateTime = cast(uint)(qw & 0xFFFFFFFF);
}

int memlen(const char *str)
{
    char *eos = cast(char*)str;
    
    while(*eos++){}
    
    return cast(int)(eos - str);
}

unittest
{
    const char* str1 = "memlen";
    const char* str2 = "memlen foo";
    
    assert (memlen(str1) == 7, "[memlen] wrong length for str1!");
    assert (memlen(str2) == 11, "[memlen] wrong length for str2!");
}

int AnsiStrToVal(const char *nptr)
{
    char* p         = cast(char*)nptr;      /* pointer to traverse string */
    int c           = cast(ubyte)*p++;
    int total       = 0;
    
    while (c >= '0' && c <= '9') 
    {
        total = 10 * total + (c - '0');     /* accumulate digit */
        c = cast(ubyte)*p++;                /* get next char */
    }
    
    return total;
}

char *ValToAnsiStr(ulong val, char *buf)
{
    char *p;                /* pointer to traverse string */
    char *firstdig;         /* pointer to first digit */
    char temp;              /* temp char */
    char *next;
    uint digval;            /* value of digit */
    
    p = buf;
    
    firstdig = p;           /* save pointer to first digit */
    
    do {
        digval = cast(uint)(val % 10);
        val /= 10;         /* get next digit */
        
        /* convert to ascii and store */
        if (digval > 9)
            *p++ = cast(char)(digval - 10 + 'a');  /* a letter */
        else
            *p++ = cast(char)(digval + '0');       /* a digit */
    } while (val > 0);
    
    /* We now have the digit of the number in the buffer, but in reverse
    order.  Thus we reverse them now. */
    
    next = p;
    *p-- = '\0';            /* terminate string; p points to last digit */
    
    do {
        temp = *p;
        *p = *firstdig;
        *firstdig = temp;   /* swap *p and *firstdig */
        --p;
        ++firstdig;         /* advance to next two digits */
    } while (firstdig < p); /* repeat until halfway */
    
    return next;
}

void ChangeSpaceToNull(ref char[] pszData)
{
    char *p = pszData.ptr;
    
    while (*p)
    {
        if (*p == 0x20)
        {
            *p = '\0';
            break;
        }
        
        p++;
    }
}

void Trim(ref string pszData)
{
    if (pszData.length == 0)
        return;
    char *p = cast(char*)pszData;
    
    while (*p)
    {
        if (*p == 0x20)
            break;
        p++;
    }
    
    pszData.length = cast(int)(p - cast(char*)pszData);
}

unittest
{
    string str = "string    ";
    Trim(str);
    assert (str == "string", "[Trim] string was not trimmed..");
}

char[] GetDate()
{
    auto currentTime = Clock.currTime();
    return format("%.4d%.2d%.2d", currentTime.year, currentTime.month, currentTime.day).dup;
}

int GetCurrentHour()
{
    auto currentTime = Clock.currTime();
    return currentTime.hour;
}

void TRACE(...)
{
    string buffer;
    void putc(dchar c) {
        buffer ~= c;
    }
    
    std.format.doFormat(&putc, _arguments, _argptr);
    buffer ~= '\0';
    std.stdio.writefln(buffer);
    //OutputDebugString( cast(char*)buffer );
}

string ArrestStringEx(in string strSource, char chSearchAfter, char chArrestBefore, out string strArrestStr)
{
    string strInnerData, strResult = string.init;
    int n, nLen = strSource.length;
    bool bGoodData = false;
    
    scope (failure) { strArrestStr = strResult = string.init; }
    
    
    strArrestStr = string.init;  /* result string */
    if ( strSource.length == 0 )
        return strResult;
    
    
    if ( nLen >= 2 )
    {
        if ( strSource[0] == chSearchAfter )
        {
            strInnerData = strSource[1 .. nLen].idup;
            nLen = strInnerData.length;
            bGoodData = true;
        }
        else
        {
            n = strSource.indexOf( chSearchAfter );
            if ( n >= 0 )
            {
                strInnerData = strSource[n + 1 .. nLen].idup;
                nLen = strInnerData.length;
                bGoodData = true;
            }
        }
    }
    
    if ( bGoodData )
    {
        n = strInnerData.indexOf( chArrestBefore );
        if ( n >= 0 )
        {
            strArrestStr = strInnerData[0 .. n].idup;
            strResult = strInnerData[n + 1 .. nLen].idup;
        }
        else
            strResult = chSearchAfter ~ strInnerData;
    }
    else
    {
        n = strSource.indexOf( chArrestBefore );
        if ( n >= 0 )
            strResult = strSource[n + 1 .. nLen].idup;
        else
            strResult = string.init;
    }
    
    return strResult;
}

unittest
{
    string s1, s2, s3;
    
    s1 = "#TestString!Yeah";
    s3 = ArrestStringEx(s1, '#', '!', s2);
    
    assert (s1 == "#TestString!Yeah", "[ArrestStringEx] wrong output for s1!");
    assert (s2 == "TestString", "[ArrestStringEx] wrong output for s2!");
    assert (s3 == "Yeah", "[ArrestStringEx] wrong output for s3!");
}

string BoolToStr(bool boo)
{
    return (boo) ? "TRUE" : "FALSE";
}

unittest
{
    assert (BoolToStr(true) == "TRUE", "[BoolToStr] wrong output!");
    assert (BoolToStr(false) == "FALSE", "[BoolToStr] wrong output!");
}

int CalcFileCheckSum(string flname)
{
    scope (failure) return 0;
    
    File reader = new File;
    // Free reader
    scope(exit) reader = null;
    reader.open(flname, FileMode.In);
    
    int filelength = cast(int)reader.size();
    
    // Read file into buffer
    ubyte[] buffer = new ubyte[filelength + 1];
    
    // Free buffer
    scope(exit) { buffer = null; }
    
    reader.readBlock( &buffer[0], filelength);
    buffer[filelength] = 0;
    
    // Calculate checksum value on buffer
    int* cval = cast(int*)buffer;
    int csum = 0;
    for (int i = 0; i < (filelength / 4); i++)
    {
        csum ^= *cval;      
        cval = cast(int*)(cast(int)cval + 4);
    }
    return csum;
}

bool CompareLStr(in string src, in string targ, size_t compn)
{
    if (compn <= 0) return false;
    if (src.length < compn) return false;
    if (targ.length < compn) return false;
    
    for (int i = 0; i < compn; i++)
    {
        if (toUpper(src[i]) != toUpper(targ[i]))
            return false;
    }
    
    return true;
}

bool CompareBackLStr(in string src, in string targ, size_t compn)
{
    if (compn <= 0) return false;
    if (src.length < compn) return false;
    if (targ.length < compn) return false;
    
    int slen = src.length - 1;
    int tlen = targ.length - 1;
    
    for (int i = 0; i < compn - 1; i++)
    {
        if (toUpper(src[slen - i]) != toUpper(targ[tlen - i]))
            return false;
    }
    
    return true;
}

string GetValidStr3(in string src, out string dest, in char[] divider)
{
    const int MAX_BUFFER_SIZE = 20480; //0x7FFF;
    
    
    string  strResult = string.init;
    char    buffer[MAX_BUFFER_SIZE];
    int     BufCount = 0, index = 0, sourcecount = src.length, ArrCount;
    char    ch = '\0';
    
    scope (failure) { dest = strResult = string.init; }
    
    
    if (sourcecount >= MAX_BUFFER_SIZE - 1)
    {
        dest = string.init;
        return strResult;
    }
    
    if (src.length == 0)
    {
        dest = string.init;
        return src;
    }
    ArrCount = divider.length / char.sizeof;
    
    while (true)
    {
        if (index < sourcecount)
        {
            ch = src[index];
            for (int i = 0; i < ArrCount; i++)
                if (ch == divider[i])
                    goto CATCH_DIV;
        }
        
        // Count > SrcLen = Buffer was filled
        if (index >= sourcecount)
        {
        CATCH_DIV:
            if (BufCount > 0)
            {
                if (BufCount < MAX_BUFFER_SIZE - 1)
                {
                    buffer[BufCount] = '\0';
                    dest = buffer[0 .. BufCount].idup;
                    
                    if (sourcecount - index > 0)
                        strResult = src[index + 1 .. sourcecount].idup;
                    else
                        strResult = src[index - sourcecount .. sourcecount].idup;
                }
                break;
            }
            else
            {
                if (index >= sourcecount)
                {
                    dest = string.init;
                    strResult = src[index + 2 .. sourcecount - 1].idup;
                    break;
                }
            }
        }
        else
        {
            if (BufCount < MAX_BUFFER_SIZE - 1)
            {
                buffer[BufCount] = ch;
                BufCount++;
            }
        }
        index++;
    }
    
    return strResult;
}

unittest
{
    string s1, s2, s3, s4;
    
    s1 = "Test#abc!xyz?";
    s1 = GetValidStr3(s1, s2, ['#','!','?']);
    s1 = GetValidStr3(s1, s3, ['#','!','?']);
    s1 = GetValidStr3(s1, s4, ['#','!','?']);
    
    assert (s1 == "", "[GetValidStr3] wrong output for s1!");   // this should be empty...
    assert (s2 == "Test", "[GetValidStr3] wrong output for s2!");
    assert (s3 == "abc", "[GetValidStr3] wrong output for s3!");
    assert (s4 == "xyz", "[GetValidStr3] wrong output for s4!");
}

int TagCount(in string strSource, char tag)
{
    int nRetValue = 0;
    foreach (c; strSource)
        if (c == tag) nRetValue++;
    return nRetValue;
}

unittest
{
    string str = "TagCount test";
    
    assert (TagCount(str, 't') == 3, "[TagCount] wrong count!");
    assert (TagCount(str, 'g') == 1, "[TagCount] wrong count!");
    assert (TagCount(str, 'c') == 0, "[TagCount] wrong count!");
}

bool IsValidIP(string addr)
{
    //create our match pattern
    static const char[] pattern = r"^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$";
    
    if (addr.length == 0)
        return false;
    
    auto r = addr.match(pattern);
    
    return !r.empty;
}

unittest
{
    string ip1 = "127.0.0.1";
    string ip2 = "127.0.695.1";
    string ip3 = "???.0.0.abc";
    string ip4 = "127.0.0.1.10";
    
    assert (IsValidIP(ip1) == true, "[IsValidIP] Correct IP reported wrong!");
    assert (IsValidIP(ip2) == false, "[IsValidIP] Bad IP reported wrong!");
    assert (IsValidIP(ip3) == false, "[IsValidIP] Bad IP reported wrong!");
    assert (IsValidIP(ip4) == false, "[IsValidIP] Bad IP reported wrong!");
}

int Str_ToInt(in string str, int def)
{
    scope (failure) return def;
    
    if (str.length == 0)
        return def;
    
    if (((str[0] >= '0') && (str[0] <= '9')) || (str[0] == '+') || (str[0] == '-'))
        return to!int(str);
    
    return def;
}

unittest
{
    string snum1 = "??";
    string snum2 = "5000";
    string snum3 = "-53";
    
    int num1 = Str_ToInt(snum1, -1);
    int num2 = Str_ToInt(snum2, -2);
    int num3 = Str_ToInt(snum3, -3);
    
    assert (num1 == -1, "[Str_ToInt] Bad number1.");
    assert (num2 == 5000, "[Str_ToInt] Bad number2.");
    assert (num3 == -53, "[Str_ToInt] Bad number3.");
}

int _MIN(int n1, int n2) @safe pure nothrow { return (n1 < n2) ? n1 : n2; }
int _MAX(int n1, int n2) @safe pure nothrow { return (n1 > n2) ? n1 : n2; }

unittest
{
    int r1 = _MIN(1, 10);
    int r2 = _MAX(50, 5);
    
    assert (r1 == 1, "[_MIN] Returned wrong number.");
    assert (r2 == 50, "[_MAX] Returned wrong number.");
}

public ushort MakeWord(ubyte A, ubyte B)
{
    return A | B << 8;
}

public int MakeLong(ushort A, ushort B)
{
    return A | B << 16;
}

public ushort LoWord(uint L)
{
    return cast(ushort)L;
}

public ushort HiWord(uint L)
{
    return L >> 16;
}

public ubyte LoByte(ushort W)
{
    return cast(ubyte)W;
}

public ubyte HiByte(ushort W)
{
    return W >> 8;
}


public R substring(R)(R range, size_t start, size_t n = cast(size_t)-1)
{
    if (n == cast(size_t)-1)
        return range[start .. $];
    else
    {
        if (start < 0)
            start = 0;
        
        size_t end;
        if (start + n > range.length)
            end = range.length;
        else
            end = start + n;
        return range[start .. end];
    }
}

unittest
{
    string str = "imastring";
    string substr;
    
    substr = substring(str, 0, 3);
    assert (substr == str[0 .. 3], "[substring 1] Returned wrong text.");
    
    substr = substring(str, 6, 3);
    assert (substr == str[6 .. 9], "[substring 2] Returned wrong text.");
    
    substr = substring(str, 0);
    assert (substr == str, "[substring 3] Returned wrong text.");
    
    substr = substring(str, 0, 0);
    assert (substr == str[0 .. 0], "[substring 4] Returned wrong text.");
    
    // Test overflow
    substr = substring(str, 5, 5);
    assert (substr == str[5 .. 9], "[substring 5] Returned wrong text.");
    
    // Test underflow
    //substr = substring(str, -2, 3);
    //assert (substr == str[0 .. 3], "[substring 6] Returned wrong text.");
}


public R removeOne(R)(R range, size_t start)
{
    auto rest = range.save;
    rest.popFrontN(start + 1);
    return range[0 .. start] ~ rest;
}

// calculates the value of A / B, in percent (rounded down)
public uint get_percentage(in uint A, in uint B)
{
    float result;
    
    if ( B == 0 )
    {
        return ~0U; // divison by zero!
    }
    
    result = 100f * (cast(float)A / cast(float)B);
    
    if ( result > uint.max )
    {
        return uint.max; // overflow!
    }
    
    return cast(uint)std.math.floor(result);
}